// Copyright (c) 2020 Advanced Micro Devices, Inc. All rights reserved
// Copyright (c) 2004-2006    ATI Technologies Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// AMD_Compress_Test_Helpers.cpp

#pragma warning(disable:4996)   // Ignoring 'fopen': This function or variable may be unsafe


#include "compressonator.h"
#include "dds_helpers.h"
#include <assert.h>
#include <stdio.h>
#include <ctype.h>

using namespace CMP;

#define MAKEFOURCC(ch0, ch1, ch2, ch3)                              \
            ((CMP_DWORD)(CMP_BYTE)(ch0) | ((CMP_DWORD)(CMP_BYTE)(ch1) << 8) |   \
            ((CMP_DWORD)(CMP_BYTE)(ch2) << 16) | ((CMP_DWORD)(CMP_BYTE)(ch3) << 24 ))

#define FOURCC_ATI1N                MAKEFOURCC('A', 'T', 'I', '1')
#define FOURCC_ATI2N                MAKEFOURCC('A', 'T', 'I', '2')
#define FOURCC_ATI2N_XY             MAKEFOURCC('A', '2', 'X', 'Y')
#define FOURCC_ATI2N_DXT5           MAKEFOURCC('A', '2', 'D', '5')
#define FOURCC_DXT5_xGBR            MAKEFOURCC('x', 'G', 'B', 'R')
#define FOURCC_DXT5_RxBG            MAKEFOURCC('R', 'x', 'B', 'G')
#define FOURCC_DXT5_RBxG            MAKEFOURCC('R', 'B', 'x', 'G')
#define FOURCC_DXT5_xRBG            MAKEFOURCC('x', 'R', 'B', 'G')
#define FOURCC_DXT5_RGxB            MAKEFOURCC('R', 'G', 'x', 'B')
#define FOURCC_DXT5_xGxR            MAKEFOURCC('x', 'G', 'x', 'R')
#define FOURCC_APC1                 MAKEFOURCC('A', 'P', 'C', '1')
#define FOURCC_APC2                 MAKEFOURCC('A', 'P', 'C', '2')
#define FOURCC_APC3                 MAKEFOURCC('A', 'P', 'C', '3')
#define FOURCC_APC4                 MAKEFOURCC('A', 'P', 'C', '4')
#define FOURCC_APC5                 MAKEFOURCC('A', 'P', 'C', '5')
#define FOURCC_APC6                 MAKEFOURCC('A', 'P', 'C', '6')
#define FOURCC_ATC_RGB              MAKEFOURCC('A', 'T', 'C', ' ')
#define FOURCC_ATC_RGBA_EXPLICIT    MAKEFOURCC('A', 'T', 'C', 'A')
#define FOURCC_ATC_RGBA_INTERP      MAKEFOURCC('A', 'T', 'C', 'I')
#define FOURCC_ETC_RGB              MAKEFOURCC('E', 'T', 'C', ' ')
#define FOURCC_BC1                  MAKEFOURCC('B', 'C', '1', ' ')
#define FOURCC_BC2                  MAKEFOURCC('B', 'C', '2', ' ')
#define FOURCC_BC3                  MAKEFOURCC('B', 'C', '3', ' ')
#define FOURCC_BC4                  MAKEFOURCC('B', 'C', '4', ' ')
#define FOURCC_BC4S                 MAKEFOURCC('B', 'C', '4', 'S')
#define FOURCC_BC4U                 MAKEFOURCC('B', 'C', '4', 'U')
#define FOURCC_BC5                  MAKEFOURCC('B', 'C', '5', ' ')
#define FOURCC_BC5S                 MAKEFOURCC('B', 'C', '5', 'S')
#define FOURCC_BC5U                 MAKEFOURCC('B', 'C', '5', 'U')


// Deprecated but still supported for decompression
#define FOURCC_DXT5_GXRB            MAKEFOURCC('G', 'X', 'R', 'B')
#define FOURCC_DXT5_GRXB            MAKEFOURCC('G', 'R', 'X', 'B')
#define FOURCC_DXT5_RXGB            MAKEFOURCC('R', 'X', 'G', 'B')
#define FOURCC_DXT5_BRGX            MAKEFOURCC('B', 'R', 'G', 'X')

#define FOURCC_DXT1  (MAKEFOURCC('D','X','T','1'))
#define FOURCC_DXT2  (MAKEFOURCC('D','X','T','2'))
#define FOURCC_DXT3  (MAKEFOURCC('D','X','T','3'))
#define FOURCC_DXT4  (MAKEFOURCC('D','X','T','4'))
#define FOURCC_DXT5  (MAKEFOURCC('D','X','T','5'))
#define FOURCC_DX10  (MAKEFOURCC('D','X','1','0'))


typedef struct {
    CMP_DWORD dwFourCC;
    CMP_FORMAT nFormat;
} CMP_FourCC;

CMP_FourCC g_FourCCs[] = {
    {FOURCC_DXT1,               CMP_FORMAT_DXT1},
    {FOURCC_DXT3,               CMP_FORMAT_DXT3},
    {FOURCC_DXT5,               CMP_FORMAT_DXT5},
    {FOURCC_DXT5_xGBR,          CMP_FORMAT_DXT5_xGBR},
    {FOURCC_DXT5_RxBG,          CMP_FORMAT_DXT5_RxBG},
    {FOURCC_DXT5_RBxG,          CMP_FORMAT_DXT5_RBxG},
    {FOURCC_DXT5_xRBG,          CMP_FORMAT_DXT5_xRBG},
    {FOURCC_DXT5_RGxB,          CMP_FORMAT_DXT5_RGxB},
    {FOURCC_DXT5_xGxR,          CMP_FORMAT_DXT5_xGxR},
    {FOURCC_DXT5_GXRB,          CMP_FORMAT_DXT5_xRBG},
    {FOURCC_DXT5_GRXB,          CMP_FORMAT_DXT5_RxBG},
    {FOURCC_DXT5_RXGB,          CMP_FORMAT_DXT5_xGBR},
    {FOURCC_DXT5_BRGX,          CMP_FORMAT_DXT5_RGxB},
    {FOURCC_ATI1N,              CMP_FORMAT_ATI1N},
    {FOURCC_ATI2N,              CMP_FORMAT_ATI2N},
    {FOURCC_ATI2N_XY,           CMP_FORMAT_ATI2N_XY},
    {FOURCC_ATI2N_DXT5,         CMP_FORMAT_ATI2N_DXT5},
    {FOURCC_BC1,                CMP_FORMAT_BC1},
    {FOURCC_BC2,                CMP_FORMAT_BC2},
    {FOURCC_BC3,                CMP_FORMAT_BC3},
    {FOURCC_BC4,                CMP_FORMAT_BC4},
    {FOURCC_BC4S,               CMP_FORMAT_BC4_S},
    {FOURCC_BC4U,               CMP_FORMAT_BC4},
    {FOURCC_BC5,                CMP_FORMAT_BC5},
    {FOURCC_BC5S,               CMP_FORMAT_BC5_S},
    {FOURCC_ATC_RGB,            CMP_FORMAT_ATC_RGB},
    {FOURCC_ATC_RGBA_EXPLICIT,  CMP_FORMAT_ATC_RGBA_Explicit},
    {FOURCC_ATC_RGBA_INTERP,    CMP_FORMAT_ATC_RGBA_Interpolated},
    {FOURCC_ETC_RGB,            CMP_FORMAT_ETC_RGB},
};
CMP_DWORD g_dwFourCCCount = sizeof(g_FourCCs) / sizeof(g_FourCCs[0]);

CMP_FORMAT GetFormat(CMP_DWORD dwFourCC) {
    for(CMP_DWORD i = 0; i < g_dwFourCCCount; i++)
        if(g_FourCCs[i].dwFourCC == dwFourCC)
            return g_FourCCs[i].nFormat;

    return CMP_FORMAT_Unknown;
}

DWORD GetFourCC(CMP_FORMAT nFormat) {
    for(DWORD i = 0; i < g_dwFourCCCount; i++)
        if(g_FourCCs[i].nFormat == nFormat)
            return g_FourCCs[i].dwFourCC;

    return 0;
}

bool IsDXT5SwizzledFormat(CMP_FORMAT nFormat) {
    if(nFormat == CMP_FORMAT_DXT5_xGBR || nFormat == CMP_FORMAT_DXT5_RxBG || nFormat == CMP_FORMAT_DXT5_RBxG ||
            nFormat == CMP_FORMAT_DXT5_xRBG || nFormat == CMP_FORMAT_DXT5_RGxB || nFormat == CMP_FORMAT_DXT5_xGxR ||
            nFormat == CMP_FORMAT_ATI2N_DXT5)
        return true;
    else
        return false;
}

typedef struct {
    CMP_FORMAT nFormat;
    const char* pszFormatDesc;
} CMP_FormatDesc;

CMP_FormatDesc g_DDSFormatDesc[] = {
    {CMP_FORMAT_Unknown,                 ("Unknown")},
    {CMP_FORMAT_RGBA_8888_S,             ("RGBA_8888_S")},
    {CMP_FORMAT_ARGB_8888,               ("ARGB_8888")},
    {CMP_FORMAT_BGRA_8888,               ("BGRA_8888")},
    {CMP_FORMAT_RGBA_8888,               ("RBGA_8888")},
    {CMP_FORMAT_RGB_888_S,               ("RGB_888_S")},
    {CMP_FORMAT_RGB_888,                 ("RGB_888")},
    {CMP_FORMAT_BGR_888,                 ("BRG_888")},
    {CMP_FORMAT_RG_8_S,                  ("RG_8_S")},
    {CMP_FORMAT_RG_8,                    ("RG_8")},
    {CMP_FORMAT_R_8_S,                   ("R_8_S")},
    {CMP_FORMAT_R_8,                     ("R_8")},
    {CMP_FORMAT_ARGB_2101010,            ("ARGB_2101010")},
    {CMP_FORMAT_ARGB_16,                 ("ARGB_16")},
    {CMP_FORMAT_RG_16,                   ("RG_16")},
    {CMP_FORMAT_R_16,                    ("R_16")},
    {CMP_FORMAT_ARGB_16F,                ("ARGB_16F")},
    {CMP_FORMAT_RG_16F,                  ("RG_16F")},
    {CMP_FORMAT_R_16F,                   ("R_16F")},
    {CMP_FORMAT_ARGB_32F,                ("ARGB_32F")},
    {CMP_FORMAT_RG_32F,                  ("RG_32F")},
    {CMP_FORMAT_R_32F,                   ("R_32F")},
    {CMP_FORMAT_DXT1,                    ("DXT1")},
    {CMP_FORMAT_DXT3,                    ("DXT3")},
    {CMP_FORMAT_DXT5,                    ("DXT5")},
    {CMP_FORMAT_DXT5_xGBR,               ("DXT5_xGBR")},
    {CMP_FORMAT_DXT5_RxBG,               ("DXT5_RxBG")},
    {CMP_FORMAT_DXT5_RBxG,               ("DXT5_RBxG")},
    {CMP_FORMAT_DXT5_xRBG,               ("DXT5_xRBG")},
    {CMP_FORMAT_DXT5_RGxB,               ("DXT5_RGxB")},
    {CMP_FORMAT_DXT5_xGxR,               ("DXT5_xGxR")},
    {CMP_FORMAT_ATI1N,                   ("ATI1N")},
    {CMP_FORMAT_ATI2N,                   ("ATI2N")},
    {CMP_FORMAT_ATI2N_XY,                ("ATI2N_XY")},
    {CMP_FORMAT_ATI2N_DXT5,              ("ATI2N_DXT5")},
    {CMP_FORMAT_BC1,                     ("BC1")},
    {CMP_FORMAT_BC2,                     ("BC2")},
    {CMP_FORMAT_BC3,                     ("BC3")},
    {CMP_FORMAT_BC4,                     ("BC4")},
    {CMP_FORMAT_BC4_S,                   ("BC4_S")},
    {CMP_FORMAT_BC5,                     ("BC5")},
    {CMP_FORMAT_BC5_S,                   ("BC5_S")},
    {CMP_FORMAT_BC6H,                    ("BC6H")},
    {CMP_FORMAT_BC7,                     ("BC7") },
    {CMP_FORMAT_ATC_RGB,                 ("ATC_RGB")},
    {CMP_FORMAT_ATC_RGBA_Explicit,       ("ATC_RGBA_Explicit")},
    {CMP_FORMAT_ATC_RGBA_Interpolated,   ("ATC_RGBA_Interpolated")},
    {CMP_FORMAT_ETC_RGB,                 ("ETC_RGB")},
};

DWORD g_dwDDSFormatDescCount = sizeof(g_DDSFormatDesc) / sizeof(g_DDSFormatDesc[0]);

static bool stringicmp( const char *left, const char *right ) {
    while ( true ) {
        char leftc = *left;
        char rightc = *right;

        if ( !leftc && !rightc ) {
            return true;
        }

        if ( !leftc || !rightc ) {
            return false;
        }

        if ( toupper( leftc ) != toupper( rightc ) ) {
            return false;
        }

        left++;
        right++;
    }

    return false;
}

CMP_FORMAT ParseFormat(const char* pszFormat) {
    if(pszFormat == NULL)
        return CMP_FORMAT_Unknown;

    for(DWORD i = 0; i < g_dwDDSFormatDescCount; i++)
        if(stringicmp(pszFormat,g_DDSFormatDesc[i].pszFormatDesc))
            return g_DDSFormatDesc[i].nFormat;

    return CMP_FORMAT_Unknown;
}

const char* GetFormatDescription(CMP_FORMAT nFormat) {
    for(DWORD i = 0; i < g_dwDDSFormatDescCount; i++)
        if(nFormat == g_DDSFormatDesc[i].nFormat)
            return g_DDSFormatDesc[i].pszFormatDesc;

    return g_DDSFormatDesc[0].pszFormatDesc;
}

#define POINTER_64 __ptr64

#pragma pack(4)

typedef struct _DDCOLORKEY {
    DWORD       dwColorSpaceLowValue;   // low boundary of color space that is to
    // be treated as Color Key, inclusive
    DWORD       dwColorSpaceHighValue;  // high boundary of color space that is
    // to be treated as Color Key, inclusive
} DDCOLORKEY;

typedef struct _DDPIXELFORMAT {
    DWORD       dwSize;                 // size of structure
    DWORD       dwFlags;                // pixel format flags
    DWORD       dwFourCC;               // (FOURCC code)
    union {
        DWORD   dwRGBBitCount;          // how many bits per pixel
        DWORD   dwYUVBitCount;          // how many bits per pixel
        DWORD   dwZBufferBitDepth;      // how many total bits/pixel in z buffer (including any stencil bits)
        DWORD   dwAlphaBitDepth;        // how many bits for alpha channels
        DWORD   dwLuminanceBitCount;    // how many bits per pixel
        DWORD   dwBumpBitCount;         // how many bits per "buxel", total
        DWORD   dwPrivateFormatBitCount;// Bits per pixel of private driver formats. Only valid in texture
        // format list and if DDPF_D3DFORMAT is set
    };
    union {
        DWORD   dwRBitMask;             // mask for red bit
        DWORD   dwYBitMask;             // mask for Y bits
        DWORD   dwStencilBitDepth;      // how many stencil bits (note: dwZBufferBitDepth-dwStencilBitDepth is total Z-only bits)
        DWORD   dwLuminanceBitMask;     // mask for luminance bits
        DWORD   dwBumpDuBitMask;        // mask for bump map U delta bits
        DWORD   dwOperations;           // DDPF_D3DFORMAT Operations
    };
    union {
        DWORD   dwGBitMask;             // mask for green bits
        DWORD   dwUBitMask;             // mask for U bits
        DWORD   dwZBitMask;             // mask for Z bits
        DWORD   dwBumpDvBitMask;        // mask for bump map V delta bits
        struct {
            WORD    wFlipMSTypes;       // Multisample methods supported via flip for this D3DFORMAT
            WORD    wBltMSTypes;        // Multisample methods supported via blt for this D3DFORMAT
        } MultiSampleCaps;

    };
    union {
        DWORD   dwBBitMask;             // mask for blue bits
        DWORD   dwVBitMask;             // mask for V bits
        DWORD   dwStencilBitMask;       // mask for stencil bits
        DWORD   dwBumpLuminanceBitMask; // mask for luminance in bump map
    };
    union {
        DWORD   dwRGBAlphaBitMask;      // mask for alpha channel
        DWORD   dwYUVAlphaBitMask;      // mask for alpha channel
        DWORD   dwLuminanceAlphaBitMask;// mask for alpha channel
        DWORD   dwRGBZBitMask;          // mask for Z channel
        DWORD   dwYUVZBitMask;          // mask for Z channel
    };
} DDPIXELFORMAT;

typedef struct _DDSCAPS2 {
    DWORD       dwCaps;         // capabilities of surface wanted
    DWORD       dwCaps2;
    DWORD       dwCaps3;
    union {
        DWORD       dwCaps4;
        DWORD       dwVolumeDepth;
    };
} DDSCAPS2;

typedef struct _DDSURFACEDESC2_64 {
    DWORD               dwSize;                 // size of the DDSURFACEDESC structure
    DWORD               dwFlags;                // determines what fields are valid
    DWORD               dwHeight;               // height of surface to be created
    DWORD               dwWidth;                // width of input surface
    union {
        LONG            lPitch;                 // distance to start of next line (return value only)
        DWORD           dwLinearSize;           // Formless late-allocated optimized surface size
    };
    union {
        DWORD           dwBackBufferCount;      // number of back buffers requested
        DWORD           dwDepth;                // the depth if this is a volume texture
    };
    union {
        DWORD           dwMipMapCount;          // number of mip-map levels requestde
        // dwZBufferBitDepth removed, use ddpfPixelFormat one instead
        DWORD           dwRefreshRate;          // refresh rate (used when display mode is described)
        DWORD           dwSrcVBHandle;          // The source used in VB::Optimize
    };
    DWORD               dwAlphaBitDepth;        // depth of alpha buffer requested
    DWORD               dwReserved;             // reserved
    DWORD               lpSurface;              // pointer to the associated surface memory
    union {
        DDCOLORKEY      ddckCKDestOverlay;      // color key for destination overlay use
        DWORD           dwEmptyFaceColor;       // Physical color for empty cubemap faces
    };
    DDCOLORKEY          ddckCKDestBlt;          // color key for destination blt use
    DDCOLORKEY          ddckCKSrcOverlay;       // color key for source overlay use
    DDCOLORKEY          ddckCKSrcBlt;           // color key for source blt use
    union {
        DDPIXELFORMAT   ddpfPixelFormat;        // pixel format description of the surface
        DWORD           dwFVF;                  // vertex format description of vertex buffers
    };
    DDSCAPS2            ddsCaps;                // direct draw surface capabilities
    DWORD               dwTextureStage;         // stage in multitexture cascade
} DDSURFACEDESC2;

#define DDSD2 DDSURFACEDESC2

static const DWORD DDS_HEADER = MAKEFOURCC('D', 'D', 'S', ' ');

bool LoadDDSFile(const char* pszFile, CMP_Texture& texture) {
    FILE* pSourceFile = fopen(pszFile, ("rb"));

    if (!pSourceFile) return false;

    DWORD dwFileHeader;
    fread(&dwFileHeader, sizeof(DWORD), 1, pSourceFile);
    if(dwFileHeader != DDS_HEADER) {
        printf("Source file is not a valid DDS.\n");
        fclose(pSourceFile);
        return false;
    }

    DDSD2 ddsd;
    fread(&ddsd, sizeof(DDSD2), 1, pSourceFile);

    memset(&texture, 0, sizeof(texture));
    texture.dwSize = sizeof(texture);
    texture.dwWidth = ddsd.dwWidth;
    texture.dwHeight = ddsd.dwHeight;
    texture.dwPitch = ddsd.lPitch;

    if(ddsd.ddpfPixelFormat.dwRGBBitCount==32) {
        // Visual Studio reports as 32bpp RGBA
        if ((ddsd.ddpfPixelFormat.dwRBitMask         == 0x000000ff)&&
                (ddsd.ddpfPixelFormat.dwGBitMask         == 0x0000ff00)&&
                (ddsd.ddpfPixelFormat.dwBBitMask         == 0x00ff0000)&&
                (ddsd.ddpfPixelFormat.dwRGBAlphaBitMask  == 0xff000000)) {
            texture.format = CMP_FORMAT_RGBA_8888;
        } else
            // Visual Studio reports as 32bpp BGRA
            if ((ddsd.ddpfPixelFormat.dwRBitMask        == 0x00ff0000) &&
                    (ddsd.ddpfPixelFormat.dwGBitMask        == 0x0000ff00) &&
                    (ddsd.ddpfPixelFormat.dwBBitMask        == 0x000000ff) &&
                    (ddsd.ddpfPixelFormat.dwRGBAlphaBitMask == 0xff000000)) {
                texture.format = CMP_FORMAT_BGRA_8888;
            }
    } else if(ddsd.ddpfPixelFormat.dwRGBBitCount==24) {
        // assumptions is made here should check all channel locations
        if (ddsd.ddpfPixelFormat.dwRBitMask == 0xff) {
            texture.format = CMP_FORMAT_RGB_888;
        } else {
            texture.format = CMP_FORMAT_BGR_888;
        }
    } else if(GetFormat(ddsd.ddpfPixelFormat.dwPrivateFormatBitCount) != CMP_FORMAT_Unknown)
        texture.format = GetFormat(ddsd.ddpfPixelFormat.dwPrivateFormatBitCount);
    else if(GetFormat(ddsd.ddpfPixelFormat.dwFourCC) != CMP_FORMAT_Unknown)
        texture.format = GetFormat(ddsd.ddpfPixelFormat.dwFourCC);
    else {
        printf("Unsupported source format.\n");
        fclose(pSourceFile);
        return false;
    }

    // Init source texture
    texture.dwDataSize = CMP_CalculateBufferSize(&texture);
    texture.pData = (CMP_BYTE*) malloc(texture.dwDataSize);

    fread(texture.pData, texture.dwDataSize, 1, pSourceFile);
    fclose(pSourceFile);

    return true;
}

enum D3D10_RESOURCE_DIMENSION {
    D3D10_RESOURCE_DIMENSION_UNKNOWN    = 0,
    D3D10_RESOURCE_DIMENSION_BUFFER    = 1,
    D3D10_RESOURCE_DIMENSION_TEXTURE1D    = 2,
    D3D10_RESOURCE_DIMENSION_TEXTURE2D    = 3,
    D3D10_RESOURCE_DIMENSION_TEXTURE3D    = 4
};

typedef struct {
    DWORD                           dxgiFormat;
    D3D10_RESOURCE_DIMENSION        resourceDimension;
    UINT                            miscFlag;                   // Used for D3D10_RESOURCE_MISC_FLAG
    UINT                            arraySize;
    UINT                            reserved;                   // Currently unused
} DDS_HEADER_DDS10;

#define DDSD_CAPS               0x00000001l     // default
#define DDSD_HEIGHT             0x00000002l
#define DDSD_WIDTH              0x00000004l
#define DDSD_PITCH              0x00000008l
#define DDSD_BACKBUFFERCOUNT    0x00000020l
#define DDSD_ZBUFFERBITDEPTH    0x00000040l
#define DDSD_ALPHABITDEPTH      0x00000080l
#define DDSD_LPSURFACE          0x00000800l
#define DDSD_PIXELFORMAT        0x00001000l
#define DDSD_CKDESTOVERLAY      0x00002000l
#define DDSD_CKDESTBLT          0x00004000l
#define DDSD_CKSRCOVERLAY       0x00008000l
#define DDSD_CKSRCBLT           0x00010000l
#define DDSD_MIPMAPCOUNT        0x00020000l
#define DDSD_REFRESHRATE        0x00040000l
#define DDSD_LINEARSIZE         0x00080000l
#define DDSD_TEXTURESTAGE       0x00100000l
#define DDSD_FVF                0x00200000l
#define DDSD_SRCVBHANDLE        0x00400000l
#define DDSD_DEPTH              0x00800000l
#define DDSD_ALL                0x00fff9eel

#define DDSCAPS_RESERVED1                       0x00000001l
#define DDSCAPS_ALPHA                           0x00000002l
#define DDSCAPS_BACKBUFFER                      0x00000004l
#define DDSCAPS_COMPLEX                         0x00000008l
#define DDSCAPS_FLIP                            0x00000010l
#define DDSCAPS_FRONTBUFFER                     0x00000020l
#define DDSCAPS_OFFSCREENPLAIN                  0x00000040l
#define DDSCAPS_OVERLAY                         0x00000080l
#define DDSCAPS_PALETTE                         0x00000100l
#define DDSCAPS_PRIMARYSURFACE                  0x00000200l
#define DDSCAPS_RESERVED3                       0x00000400l
#define DDSCAPS_PRIMARYSURFACELEFT              0x00000000l
#define DDSCAPS_SYSTEMMEMORY                    0x00000800l
#define DDSCAPS_TEXTURE                         0x00001000l
#define DDSCAPS_3DDEVICE                        0x00002000l
#define DDSCAPS_VIDEOMEMORY                     0x00004000l
#define DDSCAPS_VISIBLE                         0x00008000l
#define DDSCAPS_WRITEONLY                       0x00010000l
#define DDSCAPS_ZBUFFER                         0x00020000l
#define DDSCAPS_OWNDC                           0x00040000l
#define DDSCAPS_LIVEVIDEO                       0x00080000l
#define DDSCAPS_HWCODEC                         0x00100000l
#define DDSCAPS_MODEX                           0x00200000l
#define DDSCAPS_MIPMAP                          0x00400000l
#define DDSCAPS_RESERVED2                       0x00800000l
#define DDSCAPS_ALLOCONLOAD                     0x04000000l
#define DDSCAPS_VIDEOPORT                       0x08000000l
#define DDSCAPS_LOCALVIDMEM                     0x10000000l
#define DDSCAPS_NONLOCALVIDMEM                  0x20000000l
#define DDSCAPS_STANDARDVGAMODE                 0x40000000l
#define DDSCAPS_OPTIMIZED                       0x80000000l

#define DDSCAPS2_RESERVED4                      0x00000002L
#define DDSCAPS2_HARDWAREDEINTERLACE            0x00000000L
#define DDSCAPS2_HINTDYNAMIC                    0x00000004L
#define DDSCAPS2_HINTSTATIC                     0x00000008L
#define DDSCAPS2_TEXTUREMANAGE                  0x00000010L
#define DDSCAPS2_RESERVED1                      0x00000020L
#define DDSCAPS2_RESERVED2                      0x00000040L
#define DDSCAPS2_OPAQUE                         0x00000080L
#define DDSCAPS2_HINTANTIALIASING               0x00000100L
#define DDSCAPS2_CUBEMAP                        0x00000200L
#define DDSCAPS2_CUBEMAP_POSITIVEX              0x00000400L
#define DDSCAPS2_CUBEMAP_NEGATIVEX              0x00000800L
#define DDSCAPS2_CUBEMAP_POSITIVEY              0x00001000L
#define DDSCAPS2_CUBEMAP_NEGATIVEY              0x00002000L
#define DDSCAPS2_CUBEMAP_POSITIVEZ              0x00004000L
#define DDSCAPS2_CUBEMAP_NEGATIVEZ              0x00008000L
#define DDSCAPS2_CUBEMAP_ALLFACES ( DDSCAPS2_CUBEMAP_POSITIVEX |\
                                    DDSCAPS2_CUBEMAP_NEGATIVEX |\
                                    DDSCAPS2_CUBEMAP_POSITIVEY |\
                                    DDSCAPS2_CUBEMAP_NEGATIVEY |\
                                    DDSCAPS2_CUBEMAP_POSITIVEZ |\
                                    DDSCAPS2_CUBEMAP_NEGATIVEZ )

#define DDSCAPS2_MIPMAPSUBLEVEL                 0x00010000L
#define DDSCAPS2_D3DTEXTUREMANAGE               0x00020000L
#define DDSCAPS2_DONOTPERSIST                   0x00040000L
#define DDSCAPS2_STEREOSURFACELEFT              0x00080000L
#define DDSCAPS2_VOLUME                         0x00200000L
#define DDSCAPS2_NOTUSERLOCKABLE                0x00400000L
#define DDSCAPS2_POINTS                         0x00800000L
#define DDSCAPS2_RTPATCHES                      0x01000000L
#define DDSCAPS2_NPATCHES                       0x02000000L
#define DDSCAPS2_RESERVED3                      0x04000000L
#define DDSCAPS2_DISCARDBACKBUFFER              0x10000000L
#define DDSCAPS2_ENABLEALPHACHANNEL             0x20000000L
#define DDSCAPS2_EXTENDEDFORMATPRIMARY          0x40000000L
#define DDSCAPS2_ADDITIONALPRIMARY              0x80000000L
#define DDSCAPS3_MULTISAMPLE_MASK               0x0000001FL
#define DDSCAPS3_MULTISAMPLE_QUALITY_MASK       0x000000E0L
#define DDSCAPS3_MULTISAMPLE_QUALITY_SHIFT      5
#define DDSCAPS3_RESERVED1                      0x00000100L
#define DDSCAPS3_RESERVED2                      0x00000200L
#define DDSCAPS3_LIGHTWEIGHTMIPMAP              0x00000400L
#define DDSCAPS3_AUTOGENMIPMAP                  0x00000800L
#define DDSCAPS3_DMAP                           0x00001000L

#define DDPF_ALPHAPIXELS                        0x00000001l
#define DDPF_ALPHA                              0x00000002l
#define DDPF_FOURCC                             0x00000004l
#define DDPF_PALETTEINDEXED4                    0x00000008l
#define DDPF_PALETTEINDEXEDTO8                  0x00000010l
#define DDPF_PALETTEINDEXED8                    0x00000020l
#define DDPF_RGB                                0x00000040l
#define DDPF_COMPRESSED                         0x00000080l
#define DDPF_RGBTOYUV                           0x00000100l
#define DDPF_YUV                                0x00000200l
#define DDPF_ZBUFFER                            0x00000400l
#define DDPF_PALETTEINDEXED1                    0x00000800l
#define DDPF_PALETTEINDEXED2                    0x00001000l
#define DDPF_ZPIXELS                            0x00002000l
#define DDPF_STENCILBUFFER                      0x00004000l
#define DDPF_ALPHAPREMULT                       0x00008000l
#define DDPF_LUMINANCE                          0x00020000l
#define DDPF_BUMPLUMINANCE                      0x00040000l
#define DDPF_BUMPDUDV                           0x00080000l

typedef enum DXGI_FORMAT {
    DXGI_FORMAT_UNKNOWN                        = 0,
    DXGI_FORMAT_R32G32B32A32_TYPELESS       = 1,
    DXGI_FORMAT_R32G32B32A32_FLOAT          = 2,
    DXGI_FORMAT_R32G32B32A32_UINT           = 3,
    DXGI_FORMAT_R32G32B32A32_SINT           = 4,
    DXGI_FORMAT_R32G32B32_TYPELESS          = 5,
    DXGI_FORMAT_R32G32B32_FLOAT             = 6,
    DXGI_FORMAT_R32G32B32_UINT              = 7,
    DXGI_FORMAT_R32G32B32_SINT              = 8,
    DXGI_FORMAT_R16G16B16A16_TYPELESS       = 9,
    DXGI_FORMAT_R16G16B16A16_FLOAT          = 10,
    DXGI_FORMAT_R16G16B16A16_UNORM          = 11,
    DXGI_FORMAT_R16G16B16A16_UINT           = 12,
    DXGI_FORMAT_R16G16B16A16_SNORM          = 13,
    DXGI_FORMAT_R16G16B16A16_SINT           = 14,
    DXGI_FORMAT_R32G32_TYPELESS             = 15,
    DXGI_FORMAT_R32G32_FLOAT                = 16,
    DXGI_FORMAT_R32G32_UINT                 = 17,
    DXGI_FORMAT_R32G32_SINT                 = 18,
    DXGI_FORMAT_R32G8X24_TYPELESS           = 19,
    DXGI_FORMAT_D32_FLOAT_S8X24_UINT        = 20,
    DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS    = 21,
    DXGI_FORMAT_X32_TYPELESS_G8X24_UINT     = 22,
    DXGI_FORMAT_R10G10B10A2_TYPELESS        = 23,
    DXGI_FORMAT_R10G10B10A2_UNORM           = 24,
    DXGI_FORMAT_R10G10B10A2_UINT            = 25,
    DXGI_FORMAT_R11G11B10_FLOAT             = 26,
    DXGI_FORMAT_R8G8B8A8_TYPELESS           = 27,
    DXGI_FORMAT_R8G8B8A8_UNORM              = 28,
    DXGI_FORMAT_R8G8B8A8_UNORM_SRGB         = 29,
    DXGI_FORMAT_R8G8B8A8_UINT               = 30,
    DXGI_FORMAT_R8G8B8A8_SNORM              = 31,
    DXGI_FORMAT_R8G8B8A8_SINT               = 32,
    DXGI_FORMAT_R16G16_TYPELESS             = 33,
    DXGI_FORMAT_R16G16_FLOAT                = 34,
    DXGI_FORMAT_R16G16_UNORM                = 35,
    DXGI_FORMAT_R16G16_UINT                 = 36,
    DXGI_FORMAT_R16G16_SNORM                = 37,
    DXGI_FORMAT_R16G16_SINT                 = 38,
    DXGI_FORMAT_R32_TYPELESS                = 39,
    DXGI_FORMAT_D32_FLOAT                   = 40,
    DXGI_FORMAT_R32_FLOAT                   = 41,
    DXGI_FORMAT_R32_UINT                    = 42,
    DXGI_FORMAT_R32_SINT                    = 43,
    DXGI_FORMAT_R24G8_TYPELESS              = 44,
    DXGI_FORMAT_D24_UNORM_S8_UINT           = 45,
    DXGI_FORMAT_R24_UNORM_X8_TYPELESS       = 46,
    DXGI_FORMAT_X24_TYPELESS_G8_UINT        = 47,
    DXGI_FORMAT_R8G8_TYPELESS               = 48,
    DXGI_FORMAT_R8G8_UNORM                  = 49,
    DXGI_FORMAT_R8G8_UINT                   = 50,
    DXGI_FORMAT_R8G8_SNORM                  = 51,
    DXGI_FORMAT_R8G8_SINT                   = 52,
    DXGI_FORMAT_R16_TYPELESS                = 53,
    DXGI_FORMAT_R16_FLOAT                   = 54,
    DXGI_FORMAT_D16_UNORM                   = 55,
    DXGI_FORMAT_R16_UNORM                   = 56,
    DXGI_FORMAT_R16_UINT                    = 57,
    DXGI_FORMAT_R16_SNORM                   = 58,
    DXGI_FORMAT_R16_SINT                    = 59,
    DXGI_FORMAT_R8_TYPELESS                 = 60,
    DXGI_FORMAT_R8_UNORM                    = 61,
    DXGI_FORMAT_R8_UINT                     = 62,
    DXGI_FORMAT_R8_SNORM                    = 63,
    DXGI_FORMAT_R8_SINT                     = 64,
    DXGI_FORMAT_A8_UNORM                    = 65,
    DXGI_FORMAT_R1_UNORM                    = 66,
    DXGI_FORMAT_R9G9B9E5_SHAREDEXP          = 67,
    DXGI_FORMAT_R8G8_B8G8_UNORM             = 68,
    DXGI_FORMAT_G8R8_G8B8_UNORM             = 69,
    DXGI_FORMAT_BC1_TYPELESS                = 70,
    DXGI_FORMAT_BC1_UNORM                   = 71,
    DXGI_FORMAT_BC1_UNORM_SRGB              = 72,
    DXGI_FORMAT_BC2_TYPELESS                = 73,
    DXGI_FORMAT_BC2_UNORM                   = 74,
    DXGI_FORMAT_BC2_UNORM_SRGB              = 75,
    DXGI_FORMAT_BC3_TYPELESS                = 76,
    DXGI_FORMAT_BC3_UNORM                   = 77,
    DXGI_FORMAT_BC3_UNORM_SRGB              = 78,
    DXGI_FORMAT_BC4_TYPELESS                = 79,
    DXGI_FORMAT_BC4_UNORM                   = 80,
    DXGI_FORMAT_BC4_SNORM                   = 81,
    DXGI_FORMAT_BC5_TYPELESS                = 82,
    DXGI_FORMAT_BC5_UNORM                   = 83,
    DXGI_FORMAT_BC5_SNORM                   = 84,
    DXGI_FORMAT_B5G6R5_UNORM                = 85,
    DXGI_FORMAT_B5G5R5A1_UNORM              = 86,
    DXGI_FORMAT_B8G8R8A8_UNORM              = 87,
    DXGI_FORMAT_B8G8R8X8_UNORM              = 88,
    DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM  = 89,
    DXGI_FORMAT_B8G8R8A8_TYPELESS           = 90,
    DXGI_FORMAT_B8G8R8A8_UNORM_SRGB         = 91,
    DXGI_FORMAT_B8G8R8X8_TYPELESS           = 92,
    DXGI_FORMAT_B8G8R8X8_UNORM_SRGB         = 93,
    DXGI_FORMAT_BC6H_TYPELESS               = 94,
    DXGI_FORMAT_BC6H_UF16                   = 95,
    DXGI_FORMAT_BC6H_SF16                   = 96,
    DXGI_FORMAT_BC7_TYPELESS                = 97,
    DXGI_FORMAT_BC7_UNORM                   = 98,
    DXGI_FORMAT_BC7_UNORM_SRGB              = 99,
    DXGI_FORMAT_AYUV                        = 100,
    DXGI_FORMAT_Y410                        = 101,
    DXGI_FORMAT_Y416                        = 102,
    DXGI_FORMAT_NV12                        = 103,
    DXGI_FORMAT_P010                        = 104,
    DXGI_FORMAT_P016                        = 105,
    DXGI_FORMAT_420_OPAQUE                  = 106,
    DXGI_FORMAT_YUY2                        = 107,
    DXGI_FORMAT_Y210                        = 108,
    DXGI_FORMAT_Y216                        = 109,
    DXGI_FORMAT_NV11                        = 110,
    DXGI_FORMAT_AI44                        = 111,
    DXGI_FORMAT_IA44                        = 112,
    DXGI_FORMAT_P8                          = 113,
    DXGI_FORMAT_A8P8                        = 114,
    DXGI_FORMAT_B4G4R4A4_UNORM              = 115,
    DXGI_FORMAT_FORCE_UINT                  = 0xffffffff
} DXGI_FORMAT;

typedef enum _D3DFORMAT {
    D3DFMT_UNKNOWN              =  0,

    D3DFMT_R8G8B8               = 20,
    D3DFMT_A8R8G8B8             = 21,
    D3DFMT_X8R8G8B8             = 22,
    D3DFMT_R5G6B5               = 23,
    D3DFMT_X1R5G5B5             = 24,
    D3DFMT_A1R5G5B5             = 25,
    D3DFMT_A4R4G4B4             = 26,
    D3DFMT_R3G3B2               = 27,
    D3DFMT_A8                   = 28,
    D3DFMT_A8R3G3B2             = 29,
    D3DFMT_X4R4G4B4             = 30,
    D3DFMT_A2B10G10R10          = 31,
    D3DFMT_A8B8G8R8             = 32,
    D3DFMT_X8B8G8R8             = 33,
    D3DFMT_G16R16               = 34,
    D3DFMT_A2R10G10B10          = 35,
    D3DFMT_A16B16G16R16         = 36,

    D3DFMT_A8P8                 = 40,
    D3DFMT_P8                   = 41,

    D3DFMT_L8                   = 50,
    D3DFMT_A8L8                 = 51,
    D3DFMT_A4L4                 = 52,

    D3DFMT_V8U8                 = 60,
    D3DFMT_L6V5U5               = 61,
    D3DFMT_X8L8V8U8             = 62,
    D3DFMT_Q8W8V8U8             = 63,
    D3DFMT_V16U16               = 64,
    D3DFMT_A2W10V10U10          = 67,

    D3DFMT_UYVY                 = MAKEFOURCC('U', 'Y', 'V', 'Y'),
    D3DFMT_R8G8_B8G8            = MAKEFOURCC('R', 'G', 'B', 'G'),
    D3DFMT_YUY2                 = MAKEFOURCC('Y', 'U', 'Y', '2'),
    D3DFMT_G8R8_G8B8            = MAKEFOURCC('G', 'R', 'G', 'B'),
    D3DFMT_DXT1                 = MAKEFOURCC('D', 'X', 'T', '1'),
    D3DFMT_DXT2                 = MAKEFOURCC('D', 'X', 'T', '2'),
    D3DFMT_DXT3                 = MAKEFOURCC('D', 'X', 'T', '3'),
    D3DFMT_DXT4                 = MAKEFOURCC('D', 'X', 'T', '4'),
    D3DFMT_DXT5                 = MAKEFOURCC('D', 'X', 'T', '5'),

    D3DFMT_D16_LOCKABLE         = 70,
    D3DFMT_D32                  = 71,
    D3DFMT_D15S1                = 73,
    D3DFMT_D24S8                = 75,
    D3DFMT_D24X8                = 77,
    D3DFMT_D24X4S4              = 79,
    D3DFMT_D16                  = 80,

    D3DFMT_D32F_LOCKABLE        = 82,
    D3DFMT_D24FS8               = 83,

    D3DFMT_L16                  = 81,

    D3DFMT_VERTEXDATA           =100,
    D3DFMT_INDEX16              =101,
    D3DFMT_INDEX32              =102,

    D3DFMT_Q16W16V16U16         =110,

    D3DFMT_MULTI2_ARGB8         = MAKEFOURCC('M','E','T','1'),

    // Floating point surface formats

    // s10e5 formats (16-bits per channel)
    D3DFMT_R16F                 = 111,
    D3DFMT_G16R16F              = 112,
    D3DFMT_A16B16G16R16F        = 113,

    // IEEE s23e8 formats (32-bits per channel)
    D3DFMT_R32F                 = 114,
    D3DFMT_G32R32F              = 115,
    D3DFMT_A32B32G32R32F        = 116,

    D3DFMT_CxV8U8               = 117,
} D3DFORMAT;


void SaveDDSFile(const char* pszFile, CMP_Texture& texture) {
    FILE* pFile = fopen(pszFile, ("wb"));
    if(!pFile)
        return;

    bool DDSet = false;

    switch (texture.format) {
    case CMP_FORMAT_BC1:
    case CMP_FORMAT_BC2:
    case CMP_FORMAT_BC3:
    case CMP_FORMAT_BC4:
    case CMP_FORMAT_BC4_S:
    case CMP_FORMAT_BC5:
    case CMP_FORMAT_BC5_S:
    {
        fwrite(&DDS_HEADER, sizeof(DWORD), 1, pFile);
        DDSD2 ddsd2;
        memset(&ddsd2, 0, sizeof(DDSD2));
        ddsd2.dwSize                   = sizeof(DDSD2);
        ddsd2.dwWidth                  = texture.dwWidth;
        ddsd2.dwHeight                 = texture.dwHeight;
        ddsd2.dwMipMapCount            = 1; // For this example  code only 1 mip level is used.
        ddsd2.dwFlags                  = DDSD_CAPS|DDSD_WIDTH|DDSD_HEIGHT|DDSD_PIXELFORMAT|DDSD_MIPMAPCOUNT;
        ddsd2.dwFlags                 |= DDSD_LINEARSIZE;
        ddsd2.dwLinearSize             = texture.dwDataSize;
        ddsd2.ddpfPixelFormat.dwSize   = sizeof(DDPIXELFORMAT);
        ddsd2.ddsCaps.dwCaps           = DDSCAPS_TEXTURE;  // In this example texture are just 2D no cubemap or volume
        ddsd2.ddpfPixelFormat.dwFlags  = DDPF_FOURCC;

        switch (texture.format) {
        case CMP_FORMAT_BC1:
            ddsd2.ddpfPixelFormat.dwFourCC = FOURCC_DXT1;
            break;
        case CMP_FORMAT_BC2:
            ddsd2.ddpfPixelFormat.dwFourCC = FOURCC_DXT3;
            break;
        case CMP_FORMAT_BC3:
            ddsd2.ddpfPixelFormat.dwFourCC = FOURCC_DXT5;
            break;
        case CMP_FORMAT_BC4:
            ddsd2.ddpfPixelFormat.dwFourCC = FOURCC_BC4U;   // FOURCC_ATI1N;
            break;
        case CMP_FORMAT_BC4_S:
            ddsd2.ddpfPixelFormat.dwFourCC = FOURCC_BC4S;
            break;
        case CMP_FORMAT_BC5:
            ddsd2.ddpfPixelFormat.dwFourCC = FOURCC_BC5U;   // FOURCC_ATI2N_XY;
            break;
        case CMP_FORMAT_BC5_S:
            ddsd2.ddpfPixelFormat.dwFourCC = FOURCC_BC5S;
            break;
        }

        ddsd2.ddpfPixelFormat.dwPrivateFormatBitCount = 0;

        // Write the data
        fwrite(&ddsd2, sizeof(DDSD2), 1, pFile);
        fwrite(texture.pData, texture.dwDataSize, 1, pFile);
    }
    break;
    }

    if (!DDSet) {
        fwrite(&DDS_HEADER, sizeof(DWORD), 1, pFile);

        DDSD2 ddsd;
        memset(&ddsd, 0, sizeof(DDSD2));

        ddsd.dwSize = sizeof(DDSD2);
        ddsd.dwFlags = DDSD_CAPS|DDSD_WIDTH|DDSD_HEIGHT|DDSD_PIXELFORMAT|DDSD_MIPMAPCOUNT|DDSD_LINEARSIZE;
        ddsd.dwWidth = texture.dwWidth;
        ddsd.dwHeight = texture.dwHeight;
        ddsd.dwMipMapCount = 1;

        ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
        ddsd.ddsCaps.dwCaps = DDSCAPS_TEXTURE|DDSCAPS_COMPLEX|DDSCAPS_MIPMAP;

        // Do we have a DX9 support FourCC format
        ddsd.ddpfPixelFormat.dwFourCC = GetFourCC(texture.format);
        if(ddsd.ddpfPixelFormat.dwFourCC) {
            ddsd.dwLinearSize = texture.dwDataSize;
            ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;

            // Do we have DXT5 swizzle format
            if(IsDXT5SwizzledFormat(texture.format)) {
                ddsd.ddpfPixelFormat.dwPrivateFormatBitCount = ddsd.ddpfPixelFormat.dwFourCC;
                ddsd.ddpfPixelFormat.dwFourCC = FOURCC_DXT5;
            }

            fwrite(&ddsd, sizeof(DDSD2), 1, pFile);
            fwrite(texture.pData, texture.dwDataSize, 1, pFile);
        } else {
            // Check to save the data using DX10 file format (BC7 is used as an example of what is supported
            // and can be expanded to include other formats

            if ((texture.format == CMP_FORMAT_BC7)  ||
                    (texture.format == CMP_FORMAT_BC6H) ||
                    (texture.format == CMP_FORMAT_BC6H_SF) ) {
                ddsd.ddpfPixelFormat.dwFlags  = DDPF_FOURCC;
                ddsd.ddpfPixelFormat.dwFourCC = MAKEFOURCC('D', 'X', '1', '0');
                ddsd.lPitch = texture.dwWidth * 4;

                // Write the data
                fwrite(&ddsd, sizeof(DDSD2), 1, pFile);

                DDS_HEADER_DDS10 HeaderDDS10;
                memset(&HeaderDDS10, 0, sizeof(HeaderDDS10));

                if (texture.format == CMP_FORMAT_BC7)
                    HeaderDDS10.dxgiFormat = DXGI_FORMAT_BC7_UNORM;
                else if (texture.format == CMP_FORMAT_BC6H)
                    HeaderDDS10.dxgiFormat = DXGI_FORMAT_BC6H_UF16;
                else if (texture.format == CMP_FORMAT_BC6H_SF)
                    HeaderDDS10.dxgiFormat = DXGI_FORMAT_BC6H_SF16;

                HeaderDDS10.resourceDimension = D3D10_RESOURCE_DIMENSION_TEXTURE2D;
                HeaderDDS10.miscFlag = 0;
                HeaderDDS10.arraySize = 1;
                HeaderDDS10.reserved = 0;

                fwrite(&HeaderDDS10, sizeof(HeaderDDS10), 1, pFile);
                fwrite(texture.pData, texture.dwDataSize, 1, pFile);
            } else {
                //-------------------------------------
                // We can use DX9 file format to save
                //-------------------------------------
                switch (texture.format) {
                case CMP_FORMAT_RGBA_8888:
                    ddsd.ddpfPixelFormat.dwRBitMask         = 0xff000000;
                    ddsd.ddpfPixelFormat.dwGBitMask         = 0x00ff0000;
                    ddsd.ddpfPixelFormat.dwBBitMask         = 0x0000ff00;
                    ddsd.ddpfPixelFormat.dwRGBAlphaBitMask  = 0x000000ff;
                    ddsd.lPitch = texture.dwPitch;
                    ddsd.ddpfPixelFormat.dwRGBBitCount = 32;
                    ddsd.ddpfPixelFormat.dwFlags = DDPF_ALPHAPIXELS | DDPF_RGB;
                    break;
                case CMP_FORMAT_ARGB_8888:
                    ddsd.ddpfPixelFormat.dwRGBAlphaBitMask  = 0xff000000;
                    ddsd.ddpfPixelFormat.dwRBitMask         = 0x00ff0000;
                    ddsd.ddpfPixelFormat.dwGBitMask         = 0x0000ff00;
                    ddsd.ddpfPixelFormat.dwBBitMask         = 0x000000ff;
                    ddsd.lPitch = texture.dwPitch;
                    ddsd.ddpfPixelFormat.dwRGBBitCount = 32;
                    ddsd.ddpfPixelFormat.dwFlags = DDPF_ALPHAPIXELS | DDPF_RGB;
                    break;
                case CMP_FORMAT_RGBA_8888_S:
                    ddsd.ddpfPixelFormat.dwRBitMask        = 0x000000ff;
                    ddsd.ddpfPixelFormat.dwGBitMask        = 0x0000ff00;
                    ddsd.ddpfPixelFormat.dwBBitMask        = 0x00ff0000;
                    ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = 0xff000000;
                    ddsd.lPitch                            = texture.dwPitch;
                    ddsd.ddpfPixelFormat.dwRGBBitCount     = 32;
                    ddsd.ddpfPixelFormat.dwFlags           = 0x00080000;  // DDPF_BUMPDUDV
                    break;
                case CMP_FORMAT_RGB_888:
                    ddsd.ddpfPixelFormat.dwRBitMask = 0x00ff0000;
                    ddsd.ddpfPixelFormat.dwGBitMask = 0x0000ff00;
                    ddsd.ddpfPixelFormat.dwBBitMask = 0x000000ff;
                    ddsd.lPitch = texture.dwPitch;
                    ddsd.ddpfPixelFormat.dwRGBBitCount = 24;
                    ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB;
                    break;
                case CMP_FORMAT_RGB_888_S:
                    ddsd.ddpfPixelFormat.dwRBitMask    = 0x00ff0000;
                    ddsd.ddpfPixelFormat.dwGBitMask    = 0x0000ff00;
                    ddsd.ddpfPixelFormat.dwBBitMask    = 0x000000ff;
                    ddsd.lPitch                        = texture.dwPitch;
                    ddsd.ddpfPixelFormat.dwRGBBitCount = 24;
                    ddsd.ddpfPixelFormat.dwFlags       = DDPF_RGB;
                    break;

                case CMP_FORMAT_RG_8:
                    ddsd.ddpfPixelFormat.dwRBitMask = 0x0000ff00;
                    ddsd.ddpfPixelFormat.dwGBitMask = 0x000000ff;
                    ddsd.lPitch = texture.dwPitch;
                    ddsd.ddpfPixelFormat.dwRGBBitCount = 16;
                    ddsd.ddpfPixelFormat.dwFlags = DDPF_ALPHAPIXELS | DDPF_LUMINANCE;
                    ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = 0xff000000;
                    break;
                case CMP_FORMAT_RG_8_S:
                    ddsd.ddpfPixelFormat.dwRBitMask        = 0x0000ff00;
                    ddsd.ddpfPixelFormat.dwGBitMask        = 0x000000ff;
                    ddsd.lPitch                            = texture.dwPitch;
                    ddsd.ddpfPixelFormat.dwRGBBitCount     = 16;
                    ddsd.ddpfPixelFormat.dwFlags           = DDPF_ALPHAPIXELS | DDPF_LUMINANCE;
                    ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = 0xff000000;
                    break;
                case CMP_FORMAT_R_8:
                    ddsd.ddpfPixelFormat.dwRBitMask    = 0x000000ff;
                    ddsd.lPitch                        = texture.dwPitch;
                    ddsd.ddpfPixelFormat.dwRGBBitCount = 8;
                    ddsd.ddpfPixelFormat.dwFlags       = DDPF_LUMINANCE;
                    break;
                case CMP_FORMAT_R_8_S:
                    ddsd.ddpfPixelFormat.dwRBitMask = 0x000000ff;
                    ddsd.lPitch = texture.dwPitch;
                    ddsd.ddpfPixelFormat.dwRGBBitCount = 8;
                    ddsd.ddpfPixelFormat.dwFlags = DDPF_LUMINANCE;
                    break;

                case CMP_FORMAT_ARGB_2101010:
                    ddsd.ddpfPixelFormat.dwRBitMask = 0x000003ff;
                    ddsd.ddpfPixelFormat.dwGBitMask = 0x000ffc00;
                    ddsd.ddpfPixelFormat.dwBBitMask = 0x3ff00000;
                    ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = 0xc0000000;
                    ddsd.lPitch = texture.dwPitch;
                    ddsd.ddpfPixelFormat.dwRGBBitCount = 32;
                    ddsd.ddpfPixelFormat.dwFlags = DDPF_ALPHAPIXELS | DDPF_RGB;
                    break;

                case CMP_FORMAT_ARGB_16:
                    ddsd.dwLinearSize = texture.dwDataSize;
                    ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC | DDPF_ALPHAPIXELS;
                    ddsd.ddpfPixelFormat.dwFourCC = D3DFMT_A16B16G16R16;
                    break;

                case CMP_FORMAT_RG_16:
                    ddsd.dwLinearSize = texture.dwDataSize;
                    ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
                    ddsd.ddpfPixelFormat.dwFourCC = D3DFMT_G16R16;
                    break;

                case CMP_FORMAT_R_16:
                    ddsd.dwLinearSize = texture.dwDataSize;
                    ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
                    ddsd.ddpfPixelFormat.dwFourCC = D3DFMT_L16;
                    break;

                case CMP_FORMAT_ARGB_16F:
                    ddsd.dwLinearSize = texture.dwDataSize;
                    ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC | DDPF_ALPHAPIXELS;
                    ddsd.ddpfPixelFormat.dwFourCC = D3DFMT_A16B16G16R16F;
                    break;

                case CMP_FORMAT_RG_16F:
                    ddsd.dwLinearSize = texture.dwDataSize;
                    ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
                    ddsd.ddpfPixelFormat.dwFourCC = D3DFMT_G16R16F;
                    break;

                case CMP_FORMAT_R_16F:
                    ddsd.dwLinearSize = texture.dwDataSize;
                    ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
                    ddsd.ddpfPixelFormat.dwFourCC = D3DFMT_R16F;
                    break;

                case CMP_FORMAT_ARGB_32F:
                    ddsd.dwLinearSize = texture.dwDataSize;
                    ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC | DDPF_ALPHAPIXELS;
                    ddsd.ddpfPixelFormat.dwFourCC = D3DFMT_A32B32G32R32F;
                    break;

                case CMP_FORMAT_RG_32F:
                    ddsd.dwLinearSize = texture.dwDataSize;
                    ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
                    ddsd.ddpfPixelFormat.dwFourCC = D3DFMT_G32R32F;
                    break;

                case CMP_FORMAT_R_32F:
                    ddsd.dwLinearSize = texture.dwDataSize;
                    ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
                    ddsd.ddpfPixelFormat.dwFourCC = D3DFMT_R32F;
                    break;

                default:
                    assert(0);
                    break;
                }

                fwrite(&ddsd, sizeof(DDSD2), 1, pFile);
                fwrite(texture.pData, texture.dwDataSize, 1, pFile);
            }
        }
    } // DDSet

    fclose(pFile);
}
